package com.fable.sp.core.service.impl;

import com.fable.sp.core.domain.Container;
import com.fable.sp.core.domain.WidgetInstance;
import com.fable.sp.core.service.WidgetService;
import com.fable.sp.core.widget.*;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * .
 *
 * @author stormning on 16/8/8.
 */
public class WidgetRequestImpl implements WidgetRequest {

    private static final ContainerContext EMPTY_CONTAINER_CONTEXT = new ContainerContext() {
        @Override
        public Long getId() {
            return null;
        }

        @Override
        public Container getContainer() {
            return new Container();
        }

        @Override
        public String getProperty(String name) {
            return null;
        }

        @Override
        public Enumeration<String> getPropertyNames() {
            return Collections.emptyEnumeration();
        }

        @Override
        public String getContextInfo() {
            return null;
        }

        @Override
        public Map<Long, Object> getInstanceFieldValues() {
            return Collections.emptyMap();
        }

        @Override
        public Map<String, Object> getColumnValues() {
            return Collections.emptyMap();
        }
    };

    protected final WidgetInstance instance;
    protected final HttpServletRequest request;
    private final Map<String, List<String>> properties = new LinkedHashMap<String, List<String>>();
    private Map<String, Object> attributes = Maps.newHashMap();
    private ContainerContext containerContext;
    private WidgetService widgetService;

    public WidgetRequestImpl(WidgetInstance instance, HttpServletRequest request, ApplicationContext appContext) {
        this.instance = instance;
        this.request = request;
        this.widgetService = appContext.getBean(WidgetService.class);
        initProperties(request);
    }

    private void initProperties(HttpServletRequest request) {
        Enumeration<String> headerNames = request.getHeaderNames();
        if (headerNames != null) {
            while (headerNames.hasMoreElements()) {
                String name = headerNames.nextElement();
                String value = request.getHeader(name);
                if (value != null) {
                    properties.put(name, Lists.newArrayList(value));
                }
            }
        }
    }

    @Override
    public Object getFieldValue() {
        if (instance == null) {
            return null;
        }
        Map<Long, Object> values = containerContext.getInstanceFieldValues();
        if (values == null) {
            return null;
        }
        return values.get(instance.getId());
    }

    @Override
    public WidgetInstance getWidgetInstance() {
        return this.instance;
    }

    @Override
    public Map<String, Preference> getPreferences() {
        WidgetInstance instance = getWidgetInstance();
        List<Preference> preferences = getWidgetPreferences(instance.getWidgetName());
        if (CollectionUtils.isEmpty(preferences)) {
            return Collections.emptyMap();
        }
        Map<String, Preference> mergedPreferences = Maps.newHashMap();
        for (Preference preference : preferences) {
            String name = preference.getName();
            Preference p = new Preference();
            p.setDisplayName(preference.getDisplayName());
            p.setName(name);
            p.setValue(instance.containsAttribute(name) ? instance.getAttribute(name) : preference.getValue());
            mergedPreferences.put(p.getName(), p);
        }
        return mergedPreferences;
    }

    private List<Preference> getWidgetPreferences(String widgetName) {
        Widget widget = widgetService.getWidget(widgetName);
        if (widget instanceof ConfigurableWidget) {
            return ((ConfigurableWidget) widget).getPreferences();
        }
        return null;
    }

    @Override
    public ContainerContext getContainerContext() {
        return containerContext == null ? EMPTY_CONTAINER_CONTEXT : containerContext;
    }

    public void setContainerContext(ContainerContext containerContext) {
        this.containerContext = containerContext;
    }

    @Override
    public WidgetSession getWidgetSession() {
        return getWidgetSession(false);
    }

    @Override
    public WidgetSession getWidgetSession(boolean create) {
        return new WidgetSessionImpl(request.getSession(create), instance.getWidgetName());
    }

    @Override
    public Object getAttribute(String name) {
        return attributes.get(name);
    }

    @Override
    public Enumeration<String> getAttributeNames() {
        return new Vector<String>(attributes.keySet()).elements();
    }

    @Override
    public String getParameter(String name) {
        return request.getParameter(name);
    }

    @Override
    public Enumeration<String> getParameterNames() {
        return request.getParameterNames();
    }

    @Override
    public String[] getParameterValues(String name) {
        return request.getParameterValues(name);
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        return request.getParameterMap();
    }

    @Override
    public void setAttribute(String name, Object object) {
        attributes.put(name, object);
    }

    @Override
    public void removeAttribute(String name) {
        attributes.remove(name);
    }

    public void setProperty(String key, String value) {
        Assert.notNull(key, "Property key must not be null");
        List<String> list = new LinkedList<String>();
        list.add(value);
        this.properties.put(key, list);
    }

    public void addProperty(String key, String value) {
        Assert.notNull(key, "Property key must not be null");
        List<String> oldList = this.properties.get(key);
        if (oldList != null) {
            oldList.add(value);
        } else {
            List<String> list = new LinkedList<String>();
            list.add(value);
            this.properties.put(key, list);
        }
    }

    @Override
    public String getProperty(String key) {
        Assert.notNull(key, "Property key must not be null");
        List<String> list = this.properties.get(key);
        return (list != null && list.size() > 0 ? list.get(0) : null);
    }

    @Override
    public Enumeration<String> getProperties(String key) {
        Assert.notNull(key, "property key must not be null");
        return Collections.enumeration(this.properties.get(key));
    }

    @Override
    public Enumeration<String> getPropertyNames() {
        return Collections.enumeration(this.properties.keySet());
    }

    @Override
    public WidgetMode getWidgetMode() {
        String mode = (String) getAttribute(RenderRequest.RENDER_MODE_NAME);
        if (StringUtils.isEmpty(mode)) {
            return WidgetMode.VIEW;
        }
        return new WidgetMode(mode);
    }

}
